using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using ZoneFiveSoftware.Common.Data.Fitness;
using FilteredStatistics.Data;

namespace FilteredStatistics.Common.Controller
{
    class FilteredStatisticsFilterCriteriasProvider : IFilterCriteriaProvider
    {
        public FilteredStatisticsFilterCriteriasProvider()
        {
            PluginMain.ZoneCategoryListChanged += new PluginMain.ZoneCategoryChangedEventHandler(OnZoneCategoryChanged);

            RebuildFilterCriterias(m_CurrentLogbook);
        }

        void OnZoneCategoryChanged(object sender, IZoneCategory category)
        {
            RebuildFilterCriterias(m_CurrentLogbook);
        }

#region IFilterCriteriaProvider Members

        public IList<object> GetFilterCriterias(ILogbook logbook)
        {
            if (m_CurrentLogbook != logbook)
            {
                m_CurrentLogbook = logbook;

                RebuildFilterCriterias(m_CurrentLogbook);
            }

            return m_Criterias;
        }

        public event PropertyChangedEventHandler FilterCriteriasChanged;

#endregion

        private void RebuildFilterCriterias(ILogbook logbook)
        {
            Int32 index = 0;

            index = AddGenericCriteria(index, typeof(PostProcessPlaceholderFilterCriteria));

            if (logbook != null)
            {
                // Cadence
                foreach (IZoneCategory cadenceCategory in logbook.CadenceZones)
                {
                    index = AddSTZoneCategoryCriteria(index, STZonesFilterCriteria.STZoneTypes.Cadence, cadenceCategory);
                }

                // Climb
                foreach (IZoneCategory climbCategory in logbook.ClimbZones)
                {
                    index = AddSTZoneCategoryCriteria(index, STZonesFilterCriteria.STZoneTypes.Climb, climbCategory);
                }

                // HR
                foreach (IZoneCategory HRCategory in logbook.HeartRateZones)
                {
                    index = AddSTZoneCategoryCriteria(index, STZonesFilterCriteria.STZoneTypes.HeartRate, HRCategory);
                }

                // Power
                foreach (IZoneCategory powerCategory in logbook.PowerZones)
                {
                    index = AddSTZoneCategoryCriteria(index, STZonesFilterCriteria.STZoneTypes.Power, powerCategory);
                }

                // Speed
                foreach (IZoneCategory speedCategory in logbook.SpeedZones)
                {
                    index = AddSTZoneCategoryCriteria(index, STZonesFilterCriteria.STZoneTypes.Speed, speedCategory);
                }
            }

            // Remove excess.  We do this here since the other 3 criterias are generic and
            //  should never need to be created twice
            if (m_Criterias.Count - 3 > index)
            {
                m_Criterias.RemoveRange(index, m_Criterias.Count - index - 3);
            }

            // Splits
            index = AddGenericCriteria(index, typeof(SplitsPlaceholderFilterCriteria));
            index = AddGenericCriteria(index, typeof(TemplateSplitsPlaceholderFilterCriteria));

            // Template
            index = AddGenericCriteria(index, typeof(TemplateFilterCriteria));
        }

        protected void TriggerFilterCriteriasChanged()
        {
            if (FilterCriteriasChanged != null)
            {
                FilterCriteriasChanged(this, new PropertyChangedEventArgs("FilterCriterias"));
            }
        }

        private Int32 AddGenericCriteria(Int32 index, Type criteriaType)
        {
            ConstructorInfo constructor = criteriaType.GetConstructor(System.Type.EmptyTypes);

            Debug.Assert(constructor != null);

            if (m_Criterias.Count == index)
            {
                m_Criterias.Add(constructor.Invoke(null));
            }
            else if (!(m_Criterias[index].GetType() == criteriaType))
            {
                m_Criterias[index] = constructor.Invoke(null);
            }

            return ++index;
        }

        private Int32 AddSTZoneCategoryCriteria(Int32 index, STZonesFilterCriteria.STZoneTypes type, IZoneCategory category)
        {
            if (m_Criterias.Count == index)
            {
                m_Criterias.Add(new STZonesFilterCriteria(null, type, category));
            }
            else if (!(m_Criterias[index] is STZonesFilterCriteria) ||
                     (m_Criterias[index] as STZonesFilterCriteria).STZoneType != type ||
                     (m_Criterias[index] as STZonesFilterCriteria).ZoneCategory != category)
            {
                m_Criterias[index] = new STZonesFilterCriteria(null, type, category);
            }

            return ++index;
        }

        private List<object> m_Criterias = new List<object>();
        private ILogbook m_CurrentLogbook = null;
    }
}
